package accesstoken

import (
	"bytes"
	"encoding/json"
	"errors"
	"ethereum/system/libraries/weixin/wxconfig"
	"ethereum/system/tools/mylog"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
)

// 该access_token不可共用(无需缓存)
type authATResponse struct {
	AccessToken  string `json:"access_token"`
	ExpiresIn    int    `json:"expires_in"`
	RefreshToken string `json:"refresh_token"`
	Openid       string `json:"openid"`
	Scope        string `json:"scope"`
}

type wxError struct {
	ErrCode int    `json:"errcode"`
	ErrMsg  string `json:"errmsg"`
}

// WXUserInfo 微信用户信息
type WXUserInfo struct {
	Openid     string   `json:"openid"`     // 用户的唯一标识
	Nickname   string   `json:"nickname"`   // 用户昵称
	Sex        uint8    `json:"sex"`        // 用户的性别，值为1时是男性，值为2时是女性，值为0时是未知
	Province   string   `json:"province"`   // 用户个人资料填写的省份
	City       string   `json:"city"`       // 普通用户个人资料填写的城市
	Country    string   `json:"country"`    // 国家，如中国为CN
	Headimgurl string   `json:"headimgurl"` // 用户头像，最后一个数值代表正方形头像大小（有0、46、64、96、132数值可选，0代表640*640正方形头像），用户没有头像时该项为空。若用户更换头像，原有头像URL将失效。
	Privilege  []string `json:"privilege"`  // 用户特权信息，json 数组，如微信沃卡用户为（chinaunicom）
	Unionid    string   `json:"unionid"`    // 只有在用户将公众号绑定到微信开放平台帐号后，才会出现该字段。
}

var (
	rmQueryReg = regexp.MustCompile(`\?.*`)
)

// RemoveURLWXParam 删除微信关键参数
func RemoveURLWXParam(uri string) string {
	if u, e := url.Parse(uri); e == nil {
		redirectVals := u.Query()
		redirectVals.Del("code")
		redirectVals.Del("state")
		uri = rmQueryReg.ReplaceAllString(uri, "")
		if str := redirectVals.Encode(); str != "" {
			uri += "?" + str
		}
	}
	return uri
}

// GetOpenWeixinURL 获取微信code的url
func GetOpenWeixinURL(redirecturi string, needUserInfo bool) string {
	vals := url.Values{}
	vals.Add("appid", wxconfig.AppID)
	vals.Add("redirect_uri", RemoveURLWXParam(redirecturi))
	vals.Add("response_type", "code")
	if needUserInfo {
		vals.Add("scope", "snsapi_userinfo")
	} else {
		vals.Add("scope", "snsapi_base")
	}
	return "https://open.weixin.qq.com/connect/oauth2/authorize?" + vals.Encode() + "#wechat_redirect"
}

// GetUserOpenid 获取用户的openid
func GetUserOpenid(code string) (openid, atoken string, err error) {
	if code == "" {
		err = errors.New("code is empty")
	} else {
		_url := fmt.Sprintf("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
			wxconfig.AppID, wxconfig.Secret, code)
		if request, e := http.NewRequest(http.MethodGet, _url, nil); e != nil {
			err = e
		} else if resp, e := http.DefaultClient.Do(request); e != nil {
			err = e
		} else {
			defer resp.Body.Close()

			r := authATResponse{}
			if body, e := ioutil.ReadAll(resp.Body); e != nil {
				err = e
			} else if bytes.Contains(body, []byte(`"errcode"`)) {
				wxe := wxError{}
				if e = json.Unmarshal(body, &wxe); e == nil {
					err = errors.New(wxe.ErrMsg)
				} else {
					err = e
				}
			} else if err = json.Unmarshal(body, &r); err == nil {
				if r.Openid != "" && r.AccessToken != "" {
					openid = r.Openid
					atoken = r.AccessToken
				} else {
					mylog.LogOtherError("weixin", "GetWXUserInfo:", string(body))
					err = errors.New("返回数据不合法")
				}
			}
		}
	}
	return
}

// GetWXUserInfo 获取用户信息
func GetWXUserInfo(code string) (WXUserInfo, []byte, error) {
	var err error
	responseRaw := []byte{}
	wxinfo := WXUserInfo{}
	if openid, atoken, e := GetUserOpenid(code); e == nil {
		_url := fmt.Sprintf("https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN", atoken, openid)
		if request, e := http.NewRequest(http.MethodGet, _url, nil); e != nil {
			err = e
		} else if resp, e := http.DefaultClient.Do(request); e != nil {
			err = e
		} else {
			defer resp.Body.Close()

			if responseRaw, err = ioutil.ReadAll(resp.Body); err == nil {
				if err = json.Unmarshal(responseRaw, &wxinfo); err == nil {
					if wxinfo.Openid == "" {
						mylog.LogOtherError("weixin", "GetWXUserInfo:", string(responseRaw))
						err = errors.New("返回数据不合法")
					}
				}
			}
		}
	} else {
		err = e
	}
	return wxinfo, responseRaw, err
}
